﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WaitHandleDemo
{
    internal class Program
    {
        static WaitHandle[] waitHandles = new WaitHandle[]
        {
            new AutoResetEvent(false),
            new AutoResetEvent(false)
        };

        static Random r = new Random();
        static void Main(string[] args)
        {
            //WaitHandlerDemo();

            //AutoResetEventDemo();

            ManualResetEventDemo();

            Console.ReadKey();
        }

        static void WaitHandlerDemo()
        {
            // Queue up two tasks on two different threads;
            // wait until all tasks are completed.
            DateTime dt = DateTime.Now;
            Console.WriteLine("Main thread is waiting for BOTH tasks to complete.");
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[0]);
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[1]);
            WaitHandle.WaitAll(waitHandles);
            // The time shown below should match the longest task.
            Console.WriteLine("Both tasks are completed (time waited={0})", (DateTime.Now - dt).TotalMilliseconds);

            // Queue up two tasks on two different threads;
            // wait until any task is completed.
            dt = DateTime.Now;
            Console.WriteLine();
            Console.WriteLine("The main thread is waiting for either task to complete.");
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[0]);
            ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), waitHandles[1]);
            int index = WaitHandle.WaitAny(waitHandles);
            // The time shown below should match the shortest task.
            Console.WriteLine("Task {0} finished first (time waited={1}).", index + 1, (DateTime.Now - dt).TotalMilliseconds);
        }

        /// <summary>
        /// 是一个自动阻塞，WaitOne() 方法阻塞程序执行，Set() 方法释放信息。当释放后阻塞的代码继续执行。但下一次执行还需要等待信号
        /// 通俗的讲，WaitOne()是关门，Set()是开门。但开门之后。执行完又自动关上了，还需要开门
        /// </summary>
        static void AutoResetEventDemo()
        {
            //初始状态为关,构造函数传入 false
            AutoResetEvent are = new AutoResetEvent(false); 
            Thread t1 = new Thread(() => {
                Console.WriteLine("开始等着开门");
                are.WaitOne();
                Console.WriteLine("终于等到你");
            });
            t1.IsBackground = true;
            t1.Start();

            Console.WriteLine("按任意键开门");
            Console.ReadKey();
            are.Set();//开门
        }

        /// <summary>
        /// 表示线程同步事件，收到信号时，必须手动重置该事件。 此类不能被继
        /// 与AutoResetEvent类刚好相反，ManualResetEvent是一旦设定Set()后就一直开门，除非调用Reset()关门。Manual：手动；Reset：关门
        /// </summary>
        static void ManualResetEventDemo()
        {
            //WaitOne()还可以设置等待超时时间：
            ManualResetEvent m = new ManualResetEvent(false);
            Thread t1 = new Thread(() => {
                Console.WriteLine("等着开门");
                if (m.WaitOne(5000))
                {
                    Console.WriteLine("终于等到你了");
                }
                else
                {
                    Console.WriteLine("5秒还没等到你");
                }
            });
            t1.Start();
            Console.WriteLine("按任意键开门");
            Console.ReadKey();
            m.Set();//开门
            Console.WriteLine("终于开门了");
            m.Reset();
        }


        private static void DoTask(Object state)
        {
            AutoResetEvent are = (AutoResetEvent)state;
            int time = 1000 * r.Next(2, 10);
            Console.WriteLine("Performing a task for {0} milliseconds.", time);
            Thread.Sleep(time);
            are.Set();
        }
    }
}
